﻿using System;
using System.IO;
using System.Text.RegularExpressions;

namespace USU.MetricsMonitor.DiffParser
{
	public class BruteDiffParser
	{
		private enum State
		{
			BeforeFileHeader,
			BeforeSeparator,
			FileHeaderRead,
			NewNameRead,
			InHunkContent
		}

		private const string ExceptionMessage = "invalid diff";

		/// <summary>
		/// This method performs the full diff parsing using specified diff reader and diff
		/// processor
		/// </summary>
		/// <param name="diffReader">Reader to obtain diff from</param>
		/// <param name="diffProcessor">DiffProcessor that will receive parse events</param>
		public static void Parse(TextReader diffReader, DiffProcessor diffProcessor, string whiteList, string blackList)
		{
			string
				oldFile = "",
				newFile = "",
				line = "";
			bool
				hunkProcessing = false;

			GroupCollection
				groups = null;

			Func<string,bool> match = pattern => 
			                          	    {
			                          		Match m = Regex.Match(line,pattern);
			                          		groups = m.Groups;
			                          		return m.Success;
			                          	};
			Func<int,string> group = i => groups[i].Value;

			State state = State.BeforeFileHeader;

			while ((line = diffReader.ReadLine()) != null)
			{
				switch (state)
				{
					case State.BeforeFileHeader:

						if (match(@"^Index: "))
							state = State.BeforeSeparator;
						break;

					case State.BeforeSeparator:

						if (match(@"^=+$"))
							state = State.FileHeaderRead;
						else
							throw new FormatException(ExceptionMessage);
						break;

					case State.FileHeaderRead:

						if (match(@"^--- ([^\s]+)"))
						{
							oldFile = group(1);
							state = State.NewNameRead;
						}
						else if (match(@"^Cannot display:"))
							state = State.BeforeFileHeader;

						else if (match(@"^Index: "))
							state = State.BeforeSeparator;
						else
							throw new FormatException(ExceptionMessage);
						break;

					case State.NewNameRead:

						if (match(@"^\+\+\+ ([^\s]+)"))
						{
							newFile = group(1);
							state = State.InHunkContent;
						}
						else
							throw new FormatException(ExceptionMessage);
						break;

					case State.InHunkContent:

						if (match(@"^@@ -(\d+),(\d+) \+(\d+),(\d+) @@"))
						{
							var oldRange = new Range
							               	{
							               		StartLine = ulong.Parse(group(1)),
							               		EndLine = ulong.Parse(group(2))
							               	};
							var newRange = new Range
							               	{
							               		StartLine = ulong.Parse(group(3)),
							               		EndLine = ulong.Parse(group(4))
							               	};

							if (hunkProcessing) diffProcessor.HunkEndHandler();

							diffProcessor.HunkStartHandler(oldFile, oldRange, newFile, newRange);
							hunkProcessing = true;
						}
						else if (match(@"^\+(.+)"))
							diffProcessor.AddedLineHandler(group(1));
						else if (match(@"^-(.+)"))
							diffProcessor.DeletedLineHandler(group(1));
						else if (match(@"^ (.+)"))
							diffProcessor.ContextLineHandler(group(1));
						else
						{
							state = (match(@"^Index: ") ? State.BeforeSeparator : State.BeforeFileHeader);

							hunkProcessing = false;
							diffProcessor.HunkEndHandler();
						}
						break;
				}
			}
			if (hunkProcessing) diffProcessor.HunkEndHandler();
		}
	}
}